package com.bb.flow.service.imp.executor.node;

import com.bb.flow.api.FlowEndCallback;
import com.bb.flow.api.entries.NotifyFlowEndInfo;
import com.bb.flow.api.exception.ParseException;
import com.bb.flow.api.type.ExecuteState;
import com.bb.flow.api.type.FlowEndType;
import com.bb.flow.config.AbstractConfigNode;
import com.bb.flow.config.FlowInfo;
import com.bb.flow.config.enumtype.NodeType;
import com.bb.flow.config.enumtype.WaitType;
import com.bb.flow.config.node.ContainerConfigNode;
import com.bb.flow.config.node.ContainerInnerNode;
import com.bb.flow.service.FlowDriver;
import com.bb.flow.service.api.NodeStateCallback;
import com.bb.flow.service.exception.FlowTemplateNameNullException;
import com.bb.flow.service.exception.FlowTemplateNotFoundException;
import com.bb.flow.service.imp.AbstractNodeExecutor;
import com.bb.flow.util.Tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author bigbeard
 * 容器节点执行器
 */
public class ContainerNodeExecutor extends AbstractNodeExecutor {


    private final static Logger logger = LoggerFactory.getLogger(ContainerNodeExecutor.class);
    private static final Object lockObj = new Object();
    /**
     * 流程对象
     */
    private FlowDriver flowDriver;
    private List<FlowInfo> flowInfos = new ArrayList<>();

    /**
     * 流程对象
     *
     * @param flowDriver 流程对象
     */
    public void setFlowDriver(FlowDriver flowDriver) {
        this.flowDriver = flowDriver;
    }

    @Override
    protected String getThreadName() {
        return "container-execute-thread-%s";
    }

    /**
     * 执行容器节点,将容器节点重新组装成一个子流程,并启动子流程的执行
     *
     * @param node         执行节点
     * @param flowInfo     流程信息
     * @param nodeCallback 执行状态回调通知接口
     */
    @Override
    protected void executeNode(AbstractConfigNode node, FlowInfo flowInfo, NodeStateCallback nodeCallback) {
        if (canExecute(node)) {
            return;
        }
        this.setNodeStateCallback(nodeCallback);
        this.setCurrentFlowInfo(flowInfo);
        this.setCurrentNode(node);
        flowInfo.getFlowContext().setNodeKey(node.getNodeKey());

        notifyExecuteState(ExecuteState.EXECUTING);

        ContainerConfigNode containerConfigNode = (ContainerConfigNode) this.getCurrentNode();
        containerConfigNode.getNodes().sort(Comparator.comparingInt(ContainerInnerNode::getOrderIndex));

        String containerFlowTemplateName = flowInfo.getFlowTemplateName()
                + "_" + containerConfigNode.getNodeKey();

        int count = 0;
        flowInfos = new ArrayList<>(containerConfigNode.getNodes().size());
        for (ContainerInnerNode innerNode : containerConfigNode.getNodes()) {
            Map<String, AbstractConfigNode> flowNodes = innerNode.getFlowNodes();
            FlowInfo newFlowInfo = new FlowInfo();
            count++;
            String newFlowTemplateName = containerFlowTemplateName + "_" + count;
            String newInstanceName = flowInfo.getFlowInstanceName() + "_" + node.getNodeKey() + "_" + count;
            newFlowInfo.setFlowTemplateName(newFlowTemplateName);
            newFlowInfo.setFlowInstanceName(newInstanceName);

            newFlowInfo.setFlowTextInfo(Tools.NULL_FLOW_TEXT_INFO);
            newFlowInfo.addFlowNode(flowNodes);
            newFlowInfo.setFlowContext(flowInfo.getFlowContext());
            try {
                newFlowInfo.setParentFlow(flowInfo);
            } catch (Exception e) {
                logger.error(e.toString());
            }
            //检查是否有多个启动节点
            ArrayList<AbstractConfigNode> collect = flowNodes.values().stream().filter(AbstractConfigNode::isStartNode).collect(Collectors.toCollection(ArrayList::new));

            if (collect.size() > 1) {
                AtomicReference<String> s = new AtomicReference<>("");
                collect.forEach(x -> s.set(s + x.getNodeKey() + ","));
                logger.error("流程模版[{}]容器节点[{}]中有多个启动节点,[{}]", flowInfo.getFlowTemplateName(), node.getNodeKey(), s);
            }
            flowInfos.add(newFlowInfo);
            try {
                flowDriver.addFlowTemplate(newFlowInfo);
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }
        if (containerConfigNode.getWaitType() == WaitType.NOT_WAIT) {
            start();
            flowInfos.clear();
            super.notifyExecuteState(ExecuteState.FINISH);
        } else if (containerConfigNode.getWaitType() == WaitType.WAIT_ALL) {
            flowDriver.registerFlowEndCallback(dealFlowEndCallback);
            start();
        } else if (containerConfigNode.getWaitType() == WaitType.WAIT_TIME && containerConfigNode.getWaitTime() > 0) {
            flowDriver.registerFlowEndCallback(dealFlowEndCallback);
            start();
            Future<?> execute = execute(containerConfigNode.getWaitTime());
            try {
                execute.get(containerConfigNode.getWaitTime(), TimeUnit.SECONDS);
            } catch (ExecutionException | InterruptedException | TimeoutException e) {
                //流程超时后不取消结束事件回调
                //flowDriver.unregisterFlowEndCallback(dealFlowEndCallback);
                super.notifyExecuteState(ExecuteState.TIME_OUT);
            }
        }
    }

    private Future<?> execute(final int sec) {
        return executorService.submit(() -> {
            synchronized (this) {
                int count = sec;
                while (count > 0 && !flowInfos.isEmpty()) {
                    count--;
                    try {
                        this.wait(1000);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        });
    }

    private void start() {
        flowInfos.forEach(x -> executorService.execute(() -> {
            try {
                flowDriver.startFlow(x.getFlowTemplateName(), x.getFlowInstanceName(), x.getParentFlow(), x.getFlowContext());
            } catch (FlowTemplateNotFoundException | FlowTemplateNameNullException | ParseException e) {
                throw new RuntimeException(e);
            }
        }));
    }

    private void dealEnd(NotifyFlowEndInfo notifyFlowEndInfo) {
        synchronized (lockObj) {
            FlowInfo flowInfo = flowInfos.stream()
                    .filter(x -> x.getFlowTemplateName().equals(notifyFlowEndInfo.getFlowTemplateName())
                            || x.getFlowInstanceName().equals(notifyFlowEndInfo.getFlowInstanceName()))
                    .findFirst().orElse(null);
            if (flowInfo == null) {
                logger.info("这不是当前流程节点[{}]的事件,我不执行 {}", getCurrentNode().getNodeKey(), notifyFlowEndInfo.getFlowTemplateName());
                return;
            }
            flowDriver.removeFlowTemplate(notifyFlowEndInfo.getFlowTemplateName());
            flowInfos.stream()
                    .filter(x -> x.getFlowTemplateName().equals(notifyFlowEndInfo.getFlowTemplateName())
                            || x.getFlowInstanceName().equals(notifyFlowEndInfo.getFlowInstanceName()))
                    .findFirst().ifPresent(flowInfos::remove);

            if (flowInfos.isEmpty()) {
                if (notifyFlowEndInfo.getFlowEndType() == FlowEndType.NORMAL_END) {
                    super.notifyExecuteState(ExecuteState.FINISH);
                }
                flowDriver.unregisterFlowEndCallback(dealFlowEndCallback);
            }
            if (notifyFlowEndInfo.getFlowEndType() == FlowEndType.EXCEPTION_END) {
                super.notifyExecuteState(ExecuteState.EXCEPTION);
            } else if (notifyFlowEndInfo.getFlowEndType() == FlowEndType.NOT_EXECUTE) {
                super.notifyExecuteState(ExecuteState.NOT_EXECUTE);
            } else if (notifyFlowEndInfo.getFlowEndType() == FlowEndType.USER_CANCEL) {
                super.notifyExecuteState(ExecuteState.CANCEL);
            }
        }
    }

    /**
     * 节点类型
     *
     * @return 节点类型
     */
    @Override
    protected NodeType getAllowExecuteNodeType() {
        return NodeType.CONTAINER_NODE;
    }

    /**
     * 流程结束回调事件
     */
    private final FlowEndCallback dealFlowEndCallback = this::dealEnd;


}
